//Britable - Animations for a arduino / shiftbrite based table
//
//Acknowledgements:
//Garret Mace from Macetech (http://macetech.com/store) for the interrupt code (http://docs.macetech.com/doku.php/shiftbrite)
//Hugo Elias, for his Prlin noise description (http://freespace.virgin.net/hugo.elias/models/m_perlin.htm)
//Trollax Kinora for his 5x5 five font (http://www.dafont.com/5x5.font)
//Jared Tarbell for his idea of 5x5 space invaders (http://www.levitated.net/daily/levInvaderFractal.html)
//
//So... the code is awful, but it works. Any suggestions, tricks, whatever to make it smaller and faster will be welcomed
//You need a pushbutton on input 8 (changemodepin) to get from one scene to another 
//Currently there are 13 scenes (maxMode) and most of them are "resolution independent" They work fine independntly of the table size (defined by iRows and iCols)
//
//Project homepage: http://code.google.com/p/britable/
//Project blog (in Spanish): http://www.neuronasmuertas.com/britable/
//
//History
//2009-11-21: Small code optimizations... less memory usage
//2009-11-15: Initial release
//2009-12-21: New scenes added. Pause button.
//2010-01-14: New scenes added. 

int SB_CommandMode;
/*int SB_RedCommand;
int SB_GreenCommand;
int SB_BlueCommand;*/


#define changemodepin 7
#define stoppin 6
#define changeparamspin  X

#define latchpin 9 
#define enablepin 10
#define datapin 11
#define clockpin 13

#define iRows 5
#define iCols 5
#define totalLeds iRows * iCols
#define maxMode 23

#define maxcurrent 1000

int iLed[totalLeds][3] = {0};

char iInternalState = 0;
boolean bPressChangeMode = false;
boolean bPressStop = false;
boolean bIsStopped = false;
char iMode = 0;
float fTimer = 0;
int iFrameCounter = -1;

float maxx = 0;

char mappx[] = {0,9,10,19,20,1,8,11,18,21,2,7,12,17,22,3,6,13,16,23,4,5,14,15,24};
long lnumeros[] = {33150783L,4591758L,31504927L,32553023L,4608996L,33060926L,33062463L,32540804L,33095231L,33094719L};

char mapsnake[] = {4,3,2,1,0,9,8,7,6,5,14,13,12,11,10,19,18,17,16,15,24,23,22,21,20};
char mapspiral[] = {12,13,6,7,8,11,18,17,16,15,14,5,4,3,2,1,0,9,10,19,20,21,22,23,24};
char mapdouspiral[] = {12,11,8,7,6,5,14,15,24,23,22,21,20,13,16,17,18,19,10,9,0,1,2,3,4};
char mapcuadspiral[] = {12,7,8,9,10,19,20,11,18,21,22,23,24,17,16,15,14,5,4,13,6,3,2,1,0};
char mapsurtidor[] = {14,13,12,11,10,9,0,1,2,3,4,5,6,7,8,   19,20,21,22,23,24,15,16,17,18};
char mapsurtidor2[] = {12,11,10,19,20,21,18, 17,22,23,24,15,16, 13,14,5,4,3,6, 7,2,1,0,9,8};

void setup() {

  pinMode(datapin, OUTPUT);
  pinMode(latchpin, OUTPUT);
  pinMode(enablepin, OUTPUT);
  pinMode(clockpin, OUTPUT);
  
  pinMode(changemodepin, INPUT);
  pinMode(stoppin, INPUT);  

  SPCR = (1<<SPE)|(1<<MSTR)|(0<<SPR1)|(0<<SPR0);

  digitalWrite(latchpin, LOW);
  digitalWrite(enablepin, LOW);
  
  //Serial.begin(57600);

}


void loop() {
  
  bPressChangeMode = (digitalRead(changemodepin))==HIGH;
  bPressStop = (digitalRead(stoppin))==HIGH;  
  
  switch(iInternalState) {
    case 0:
      if (bPressChangeMode) {
        iInternalState = 1;
        iMode++;
        if (iMode >= maxMode) iMode = 0; 
        background(0,0,0);
        plot(iMode % iCols, iMode / iCols, 100, 100, 100);
      } else if (bPressStop) {
        iInternalState = 2;
        bIsStopped = !bIsStopped;
      } else {
        if (!bIsStopped) {        
          switch(iMode){
            case 0:
              if (++iFrameCounter % 150 != 0) break;
              iFrameCounter = 0;          
              spaceInvader();          
              break;
            case 1:
              if (++iFrameCounter % 50 != 0) break;
              iFrameCounter = 0;          
              numeros();          
              break;            
            case 2:
              perlinAll(0.001);          
              break;
            case 3:
              perlinRGB(0.008);          
              break;            
            case 4:
              perlinRain(0.01);          
              break;                        
            case 5:
              perlinDampenBorder(0.01);          
              break;                                    
            case 6:
              perlinDampenMiddle(0.004);          
              break;          
            case 7:
              perlinSnake(0.03,0.2,-0.5);          
              break;                                       
            case 8:              
              perlinSnake(0.015,0.15,-0.1);          
              break;                         
            case 9:
              perlinSpiral(0.04, 0.2, -0.65);          
              break;                 
            case 10:              
              perlinSpiral(0.012, 0.2, 0.4);                        
              break;                 
            case 11:
              perlinDoubleSpiral(0.02, 0.2, -0.7);          
              break;
            case 12:              
              perlinDoubleSpiral(0.012, 0.15, 0.4);          
              break;                             
            case 13:
              perlinQuadSpiral(0.01, 0.2, 0.75);          
              break;              
            case 14:              
              perlinQuadSpiral(0.003, 0.1, 0.1);          
              break;              
            case 15:
              perlinLine(0.025, 0.0625, 0.7);          
              break;
            case 16:
              perlinLine(0.0075, 0.0625, 0.1);          
              break;  
            case 17:
              clockSeconds();          
              break;             
            case 18:
              perlinFireball(0.02);          
              break;                                     
            case 19:
              perlinSurtidor(0.008, 0.1, 0.4);          
              break;                                                   
            case 20:
              perlinSurtidor(0.005, 0.1, 0.1);          
              break;
            case 21:
              perlinSurtidor2(0.008, 0.25, 0.6);          
              break;                                                   
            case 22:
              perlinSurtidor2(0.001, 0.5, 0.15);          
              break;                                                                                             
          }   
        }
      }
      break;
    case 1:
      if (!bPressChangeMode) {
        iInternalState = 0;
        iFrameCounter = -1;
      }    
      break;
    case 2:
      if (!bPressStop) {
        iInternalState = 0;
      }    
      break;         
  }

  SB_WriteLEDData();
  delay(20);  
}

/*-----------------------------------------------------------------*/
void spaceInvader(){
  background(0,0,0);
  int iInvader = (int)random(0,32768);
  int iCurrMask = 16384;
  for (char i=0; i<15; i++) {
    if ((iInvader & iCurrMask)!=0) {
      char x = i % 3;
      char y = i / 3;
      plot(x, y, maxcurrent, maxcurrent, 150);
      if (x!=2) { plot(4-x, y, maxcurrent, maxcurrent, 150); }     
    }
    iCurrMask = iCurrMask >> 1;
  }    
}
/*-----------------------------------------------------------------*/


/*-----------------------------------------------------------------*/
void numeros(){
  background(0,0,0);
  int iNumeros = (int)random(0,10);
  long lCurrMask = 16777216;
  for (char i=0; i<25; i++) {
    if ((lnumeros[iNumeros] & lCurrMask)!=0) {
      char x = i % 5;
      char y = i / 5;
      plot(x, y, 0, 0, maxcurrent);
    }
    lCurrMask = lCurrMask >> 1;
  }    
}
/*-----------------------------------------------------------------*/


/*-----------------------------------------------------------------*/
float perlin_noise_2d(int x, int y) {
  long n=(long)x+(long)y*57;
  n = (n<<13)^ n;
  return 1.0 - (((n *(n * n * 15731L + 789221L) + 1376312589L)  & 0x7fffffff) / 1073741824.0);    
}

float perlin_map(float x, float in_min, float in_max, float out_min, float out_max){
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

float perlin_scale(float x, float startval, float endval){
  //valido porque x vale [0,1]
  return x * (endval - startval) + startval;  
}

float perlin_function(float x, float y){
  int fx = floor(x);
  int fy = floor(y);
  float s,t,u,v;
  s=perlin_noise_2d(fx,fy);
  t=perlin_noise_2d(fx+1,fy);
  u=perlin_noise_2d(fx,fy+1);
  v=perlin_noise_2d(fx+1,fy+1);
  float inter1 = perlin_scale(x-fx,s,t);
  float inter2 = perlin_scale(x-fx,u,v);  
  return perlin_scale(y-fy,inter1,inter2);  
}

void perlinAll(float delta){
  float pr = perlin_function(fTimer,0);
  float pg = perlin_function(fTimer,1);      
  float pb = perlin_function(fTimer,2);      
  int r = perlin_dampen_function(pr,0.55,maxcurrent);
  int g = perlin_dampen_function(pg,0.55,maxcurrent);
  int b = perlin_dampen_function(pb,0.55,maxcurrent); 
  background(r,g,b);      
  //background(maxcurrent,maxcurrent,maxcurrent);        
  fTimer += delta;
}

void perlinRGB(float delta){
  for (char y=0; y<iRows;y++){    
    for (char x=0; x<iCols;x++){
      float pr = perlin_function(fTimer+x*0.5,y+1);
      float pg = perlin_function(-fTimer+x*0.25,y*0.5);      
      float pb = perlin_function(x,fTimer+y);            
      int r= perlin_dampen_function(pr,0.65,maxcurrent);
      int g= perlin_dampen_function(pg,0.65,maxcurrent);      
      int b= perlin_dampen_function(pb,0.65,maxcurrent);                  
      plot(x,y,r,g,b);      
    }
  }
  fTimer += delta;
}

void perlinRain(float delta){
  for (char y=0; y<iRows;y++){    
    for (char x=0; x<iCols;x++){
      float fperlin = perlin_function(x+5,y+perlin_function(fTimer,10)+fTimer);
      int r = perlin_limit_function(fperlin,.5,maxcurrent);                  
      plot(x,y,r,r,r);      
    }
  }
  fTimer += delta;
}

void perlinDampenBorder(float delta){
  for (char y=0; y<iRows;y++){    
    for (char x=0; x<iCols;x++){
      float pr = perlin_function(fTimer+x*0.25,y*0.25+1);
      float pg = perlin_function(-fTimer+x*0.25,y*0.25);
      float pb = perlin_function(x*0.25,fTimer+y*0.25);
      int r = perlin_limit_function(pr,0,maxcurrent);
      int g = perlin_limit_function(pg,0,maxcurrent);
      int b = perlin_limit_function(pb,0,maxcurrent);
      plot(x,y,r,g,b);      
    }
  }
  fTimer += delta;
}

void perlinDampenMiddle(float delta){
  for (char y=0; y<iRows;y++){    
    for (char x=0; x<iCols;x++){
      float pr = perlin_function(fTimer+x*0.25,y*0.25+1);
      float pg = perlin_function(-fTimer+x*0.25,y*0.25);
      float pb = perlin_function(x*0.25,fTimer+y*0.25);
      int r = perlin_dampen_function(pr,0.1,maxcurrent);
      int g = perlin_dampen_function(pg,0.1,maxcurrent);
      int b = perlin_dampen_function(pb,0.1,maxcurrent);      
      plot(x,y,r,g,b);      
    }
  }
  fTimer += delta;
}

void perlinSnake(float delta, float scale, float dampen){
  for (char i=0; i<25; i++) {
    float pr = perlin_function(-fTimer+i*scale,1);    
    float pg = perlin_function(-fTimer+i*scale,3);        
    float pb = perlin_function(-fTimer+i*scale,7);            
    iLed[mapsnake[i]][0] = perlin_limit_function(pr,dampen,maxcurrent);      
    iLed[mapsnake[i]][1] = perlin_limit_function(pg,dampen,maxcurrent);      
    iLed[mapsnake[i]][2] = perlin_limit_function(pb,dampen,maxcurrent);     
  }
  fTimer += delta;  
}

void perlinSpiral(float delta, float scale, float dampen){
  for (char i=0; i<25; i++) {
    float pr = perlin_function(-fTimer+i*scale,1);    
    float pg = perlin_function(-fTimer+i*scale,3);        
    float pb = perlin_function(-fTimer+i*scale,7);            
    iLed[mapspiral[i]][0] = perlin_limit_function(pr,dampen,maxcurrent);      
    iLed[mapspiral[i]][1] = perlin_limit_function(pg,dampen,maxcurrent);      
    iLed[mapspiral[i]][2] = perlin_limit_function(pb,dampen,maxcurrent);      
  }
  fTimer += delta;  
}

void perlinDoubleSpiral(float delta, float scale, float dampen){
  for (char i=0; i<13; i++) {
    float pr = perlin_function(-fTimer+i*scale,1);    
    float pg = perlin_function(-fTimer+i*scale,3);        
    float pb = perlin_function(-fTimer+i*scale,7);            
    iLed[mapdouspiral[i+12]][0] = iLed[mapdouspiral[i]][0] = perlin_limit_function(pr,dampen,maxcurrent);      
    iLed[mapdouspiral[i+12]][1] = iLed[mapdouspiral[i]][1] = perlin_limit_function(pg,dampen,maxcurrent);      
    iLed[mapdouspiral[i+12]][2] = iLed[mapdouspiral[i]][2] = perlin_limit_function(pb,dampen,maxcurrent);          
  }
  fTimer += delta;  
}

void perlinQuadSpiral(float delta, float scale, float dampen){
  for (char i=0; i<7; i++) {
    float pr = perlin_function(-fTimer+i*scale,1);    
    float pg = perlin_function(-fTimer+i*scale,3);        
    float pb = perlin_function(-fTimer+i*scale,7);            
    iLed[mapcuadspiral[i+18]][0] = iLed[mapcuadspiral[i+12]][0] = iLed[mapcuadspiral[i+6]][0] = iLed[mapcuadspiral[i]][0] = perlin_dampen_function(pr,dampen,maxcurrent);      
    iLed[mapcuadspiral[i+18]][1] = iLed[mapcuadspiral[i+12]][1] = iLed[mapcuadspiral[i+6]][1] = iLed[mapcuadspiral[i]][1] = perlin_dampen_function(pg,dampen,maxcurrent);      
    iLed[mapcuadspiral[i+18]][2] = iLed[mapcuadspiral[i+12]][2] = iLed[mapcuadspiral[i+6]][2] = iLed[mapcuadspiral[i]][2] = perlin_dampen_function(pb,dampen,maxcurrent);          
  }
  fTimer += delta;  
}

void perlinLine(float delta, float scale, float dampen){
  for (char y=0; y<iRows;y++){    
    for (char x=0; x<iCols;x++){
      float iMul = (y % 2)? -1: 1;
      float pr = perlin_function(   x*scale-fTimer*iMul,y*7);
      float pg = perlin_function(17+x*scale-fTimer*iMul,y*7);      
      float pb = perlin_function(11+x*scale-fTimer*iMul,y*7);            
      int r= perlin_dampen_function(pr,dampen,maxcurrent);
      int g= perlin_dampen_function(pg,dampen,maxcurrent);      
      int b= perlin_dampen_function(pb,dampen,maxcurrent);                  
      plot(x,y,r,g,b);      
    }
  }
  fTimer += delta;
}


void perlinFireball(float delta){
  
  int r = 1023;
  int g = 170;
  int b = 6;
  
  background(0,0,0);  
  
  float x = (perlin_function(fTimer,0) + 1)* 1.99999;
  float y = (perlin_function(fTimer,2) + 1)* 1.99999;  
  
  int fx = floor(x);
  int fy = floor(y);

  plot(fx,  fy,  r*(1-x+fx)*(1-y+fy),g*(1-x+fx)*(1-y+fy),b*(1-x+fx)*(1-y+fy)); 
  plot(fx+1,fy,  r*(x-fx)*(1-y+fy),  g*(x-fx)*(1-y+fy),  b*(x-fx)*(1-y+fy)); 
  plot(fx,  fy+1,r*(1-x+fx)*(y-fy),g*(1-x+fx)*(y-fy),b*(1-x+fx)*(y-fy)); 
  plot(fx+1,fy+1,r*(x-fx)*(y-fy),  g*(x-fx)*(y-fy),  b*(x-fx)*(y-fy)); 
  
  fTimer += delta;
}


void perlinSurtidor(float delta, float scale, float dampen){
  for (char i=0; i<15; i++) {
    float pr = perlin_function(-fTimer+i*scale,1);    
    float pg = perlin_function(-fTimer+i*scale,3);        
    float pb = perlin_function(-fTimer+i*scale,7);            
    iLed[mapsurtidor[i+10]][0] = iLed[mapsurtidor[i]][0] = perlin_dampen_function(pr,dampen,maxcurrent);     
    iLed[mapsurtidor[i+10]][1] = iLed[mapsurtidor[i]][1] = perlin_dampen_function(pg,dampen,maxcurrent);         
    iLed[mapsurtidor[i+10]][2] = iLed[mapsurtidor[i]][2] = perlin_dampen_function(pb,dampen,maxcurrent);          
  }
  fTimer += delta;  
}

void perlinSurtidor2(float delta, float scale, float dampen){
  for (char i=0; i<7; i++) {
    float pr = perlin_function(-fTimer+i*scale,1);    
    float pg = perlin_function(-fTimer+i*scale,3);        
    float pb = perlin_function(-fTimer+i*scale,7);            
    iLed[mapsurtidor2[i+18]][0] = iLed[mapsurtidor2[i+12]][0] = iLed[mapsurtidor2[i+6]][0] = iLed[mapsurtidor2[i]][0] = perlin_dampen_function(pr,dampen,maxcurrent);     
    iLed[mapsurtidor2[i+18]][1] = iLed[mapsurtidor2[i+12]][1] = iLed[mapsurtidor2[i+6]][1] = iLed[mapsurtidor2[i]][1] = perlin_dampen_function(pg,dampen,maxcurrent);         
    iLed[mapsurtidor2[i+18]][2] = iLed[mapsurtidor2[i+12]][2] = iLed[mapsurtidor2[i+6]][2] = iLed[mapsurtidor2[i]][2] = perlin_dampen_function(pb,dampen,maxcurrent);          
  }
  fTimer += delta;  
}

int perlin_limit_function(float pv, float threshold, int maxlight){
  return pv > threshold ? (int)perlin_map(pv,threshold,1,0,maxlight) : 0;
}

int perlin_dampen_function(float pv, float threshold, int maxlight){
  if (pv<-threshold || pv>threshold) return 0;
  if (pv>=-threshold && pv<0) 
    return (int)perlin_map(pv,-threshold,0,0,maxlight);   
  else 
    return (int)perlin_map(pv,0,threshold,maxlight,0);   
}
/*-----------------------------------------------------------------*/


/*-----------------------------------------------------------------*/
void clockSeconds(){

  background(0,0,0);
  unsigned long ulm = millis();

  unsigned long iModMillis = ulm % 6000;
  unsigned long iSeconds = (ulm/6000) % 10;
  for (char i=0; i<iSeconds; i++) {
    char x = i % 5;
    char y = i / 5;    
    plot(x, y, 240,60,0);
  }    
  int pr = perlin_map(iModMillis, 0, 5999, 0, 240);
  int pg = perlin_map(iModMillis, 0, 5999, 0, 60);  
  plot(iSeconds % 5, iSeconds / 5, pr, pg, 0);

  unsigned long iModSeconds = (ulm % 60000UL);    
  unsigned long iMinutes = (ulm/60000UL) % 15;  
  for (char i=0; i<iMinutes; i++) {
    char x = i % 5;
    char y = i / 5;    
    plot(x, y+2, 460,16,0);
  }    
  pr = perlin_map(iModSeconds, 0, 59999, 0, 460);
  pg = perlin_map(iModSeconds, 0, 59999, 0, 16);  
  plot(iMinutes % 5, 2+iMinutes / 5, pr, pg, 0);  
}
/*-----------------------------------------------------------------*/


void SB_SendPacket(int SB_RedCommand, int SB_GreenCommand, int SB_BlueCommand) {
//void SB_SendPacket() {
  if (SB_CommandMode == B01){
    SB_RedCommand =  99;
    SB_GreenCommand = 99;
    SB_BlueCommand = 99;
  }
  SPDR = SB_CommandMode << 6 | SB_BlueCommand>>4;
  while(!(SPSR & (1<<SPIF)));

  SPDR = SB_BlueCommand<<4 | SB_RedCommand>>6;
  while(!(SPSR & (1<<SPIF)));

  SPDR = SB_RedCommand << 2 | SB_GreenCommand>>8;
  while(!(SPSR & (1<<SPIF)));

  SPDR = SB_GreenCommand;
  while(!(SPSR & (1<<SPIF)));

}


void SB_WriteLEDData() {
 
  SB_CommandMode = B00;
  //for (int f=0; f<totalLeds; f++) SB_SendPacket(B00, iLed[f][0], iLed[f][1], iLed[f][2]);
  for (int f=0; f<totalLeds; f++) {
    //SB_RedCommand =   iLed[f][0];
    //SB_GreenCommand =  iLed[f][1];
    //SB_BlueCommand =  iLed[f][2];    
    //SB_SendPacket();    
    SB_SendPacket( iLed[f][0], iLed[f][1], iLed[f][2]);

  }
  delayMicroseconds(5);
  digitalWrite(latchpin,HIGH);
  delayMicroseconds(5);
  digitalWrite(latchpin,LOW);

  SB_CommandMode = B01;
  for (int f=0; f< totalLeds; f++) /*SB_SendPacket();*/ SB_SendPacket(0,0,0);
  delayMicroseconds(5);
  digitalWrite(latchpin,HIGH);
  delayMicroseconds(5);
  digitalWrite(latchpin,LOW);

}

void plot(int x, int y, int r, int g, int b){
  iLed[mappx[y*iCols+x]][0] = r;
  iLed[mappx[y*iCols+x]][1] = g;
  iLed[mappx[y*iCols+x]][2] = b;  
}

void background(int r, int g, int b){
  for (int f=0; f<totalLeds; f++){
    iLed[f][0] = r;
    iLed[f][1] = g;
    iLed[f][2] = b;                       
  }       
}



